package com.stdz.counter.utils;

import com.stdz.counter.core.constant.DataMode;
import com.stdz.counter.core.constant.MeterModel;
import com.stdz.counter.core.exception.ResultException;
import com.stdz.counter.core.pool.ClientPool;
import com.stdz.counter.entity.Meter;
import com.stdz.counter.entity.MeterInfo;
import de.re.easymodbus.exceptions.ModbusException;
import de.re.easymodbus.modbusclient.ModbusClient;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Map;

public class ModbusHelper {

    private static int OPERATE_POS = 0x0;

    private static int UPPER_ROW_CLEAR = 0x2;

    private static int LOWER_ROW_CLEAR = 0x1;

    private static int PAUSE = 0x4;

    private static int CONTINUE = 0x8;

    private static transient Logger logger = LoggerFactory.getLogger(ModbusHelper.class);

    public static void initMeterInfo(MeterInfo meterInfo, Map map) throws ResultException {
        try {
            ModbusClient modbusClient = ClientPool.getInstance().createModbusClient(meterInfo.getComPort());
            while (modbusClient == null) {
                Thread.sleep(500L);
                modbusClient = ClientPool.getInstance().createModbusClient(meterInfo.getComPort());
            }
            MeterModel meterModel = MeterModel.getMeterModel(meterInfo.getMeterModel());
            try {
                if (map.containsKey("alarm1")) {
                    String alarm1 = map.get("alarm1").toString();
                    if (StringUtils.isNotBlank(alarm1)) {
                        ModbusHelper.writeAlarm1(modbusClient, meterModel, meterInfo.getAlarm1());
                    }
                }
                if (map.containsKey("alarm2")) {
                    String alarm2 = map.get("alarm2").toString();
                    if (StringUtils.isNotBlank(alarm2)) {
                        ModbusHelper.writeAlarm2(modbusClient, meterModel, meterInfo.getAlarm2());
                    }
                }
                if (map.containsKey("scaleRate")) {
                    String scaleRate = map.get("scaleRate").toString();
                    if (StringUtils.isNotBlank(scaleRate)) {
                        ModbusHelper.writeScaleRate(modbusClient, meterModel, map.get("scaleRate").toString());
                    }
                }
            } catch (Exception e) {
                logger.error("initException:", e);
                throw new ResultException("设备初始化异常");
            } finally {
                ClientPool.getInstance().releaseModbusClient(meterInfo.getComPort(), modbusClient);
            }
        } catch (ResultException e) {
            throw e;
        } catch (Exception e) {
            logger.error("initException:", e);
            throw new ResultException("设备初始化异常");
        }
    }

    /**
     *
     * @param comPort
     * @return 1：成功
     */
    public static int singleClear1(String comPort) {
        ModbusClient modbusClient = null;
        try {
            modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            while (modbusClient == null) {
                Thread.sleep(500L);
                modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            }
            modbusClient.WriteSingleRegister(OPERATE_POS, UPPER_ROW_CLEAR);
            return 1;
        } catch (Throwable t) {
            logger.error("single clear1 exception:", t);
            return 0;
        } finally {
            ClientPool.getInstance().releaseModbusClient(comPort, modbusClient);
        }
    }

    /**
     *
     * @param modbusClient
     * @return 1：成功
     */
    public static int singleClear1(ModbusClient modbusClient) {
        if (modbusClient == null) {
            return 0;
        }
        try {
            modbusClient.WriteSingleRegister(OPERATE_POS, UPPER_ROW_CLEAR);
            return 1;
        } catch (Throwable t) {
            logger.error("single clear1 exception:", t);
            return 0;
        }
    }

    public static int singleClear2(String comPort) {
        ModbusClient modbusClient = null;
        try {
            modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            while (modbusClient == null) {
                Thread.sleep(500L);
                modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            }
            modbusClient.WriteSingleRegister(OPERATE_POS, LOWER_ROW_CLEAR);
            return 1;
        } catch (Throwable t) {
            logger.error("single clear2 exception:", t);
            return 0;
        } finally {
            ClientPool.getInstance().releaseModbusClient(comPort, modbusClient);
        }
    }

    public static int singleClear2(ModbusClient modbusClient) {
        if (modbusClient == null) {
            return 0;
        }
        try {
            modbusClient.WriteSingleRegister(OPERATE_POS, LOWER_ROW_CLEAR);
            return 1;
        } catch (Throwable t) {
            logger.error("single clear2 exception:", t);
            return 0;
        }
    }

    public static int bothClear(String comPort) {
        ModbusClient modbusClient = null;
        try {
            modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            while (modbusClient == null) {
                Thread.sleep(500L);
                modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            }
            modbusClient.WriteSingleRegister(OPERATE_POS, UPPER_ROW_CLEAR + LOWER_ROW_CLEAR);
            return 1;
        } catch (Throwable t) {
            logger.error("both clear exception:", t);
            return 0;
        } finally {
            ClientPool.getInstance().releaseModbusClient(comPort, modbusClient);
        }
    }

    public static int bothClear(ModbusClient modbusClient) {
        if (modbusClient == null) {
            return 0;
        }
        try {
            modbusClient.WriteSingleRegister(OPERATE_POS, UPPER_ROW_CLEAR + LOWER_ROW_CLEAR);
            return 1;
        } catch (Throwable t) {
            logger.error("both clear exception:", t);
            return 0;
        }
    }

    public static int readUpperRowData(ModbusClient modbusClient, MeterModel meterModel, MeterInfo meterInfo) {
        int pos = meterModel.getDataPos(meterInfo.getDataMode());
        if (meterInfo.getModbusPos() > 0) {
            pos = meterInfo.getModbusPos();
        }
        int[] datas = readData(modbusClient, pos, 2);
        if (meterInfo.getDataMode() == DataMode.BCD.getMode()) {
            return mergeBCDIntData(datas);
        } else {
            return mergeIntData(datas);
        }
    }

    public static int readLowerRowData(ModbusClient modbusClient, MeterModel meterModel, MeterInfo meterInfo) {
        int pos = meterModel.getDataPos(meterInfo.getDataMode());
        if (meterInfo.getModbusPos() > 0) {
            pos = meterInfo.getModbusPos();
        }
        pos = pos + 2;
        int[] datas = readData(modbusClient, pos, 2);
        if (meterInfo.getDataMode() == DataMode.BCD.getMode()) {
            return mergeBCDIntData(datas);
        } else {
            return mergeIntData(datas);
        }
    }

//    public static int readScaleRate(ModbusClient modbusClient, MeterModel meterModel, MeterInfo meterInfo) {
//        int pos = meterModel.getDataPos(meterInfo.getDataMode());
//        int[] datas = readData(modbusClient, pos, 1);
//        if (meterInfo.getDataMode() == DataMode.BCD.getMode()) {
//            return mergeBCDIntData(datas);
//        } else {
//            return mergeIntData(datas);
//        }
//    }

    public static int writeScaleRate(String comPort, MeterModel meterModel, String scaleRate) {
        ModbusClient modbusClient = null;
        try {
            modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            if (modbusClient == null) {
                return 0;
            }
            int[] datas = splitMeterData(meterModel, scaleRate, true);
            modbusClient.WriteMultipleRegisters(meterModel.getScalePos(), datas);
            return 1;
        } catch (Throwable t) {
            logger.error("write scale rete exception:", t);
            return 0;
        } finally {
            ClientPool.getInstance().releaseModbusClient(comPort, modbusClient);
        }
    }

    /**
     *
     * @param modbusClient
     * @param meterModel
     * @param scaleRate 实际展示值
     * @return
     */
    public static int writeScaleRate(ModbusClient modbusClient, MeterModel meterModel, String scaleRate) {
        if (modbusClient == null) {
            return 0;
        }
        try {
            int[] datas = splitMeterData(meterModel, scaleRate, true);
            modbusClient.WriteMultipleRegisters(meterModel.getScalePos(), datas);
            return 1;
        } catch (Throwable t) {
            logger.error("write scale rete exception:", t);
            return 0;
        }
    }

    public static int readScaleRate(ModbusClient modbusClient, MeterModel meterModel) {
        try {
            int[] datas = modbusClient.ReadHoldingRegisters(meterModel.getScalePos(), 2);
            return mergeMeterDatas(meterModel, datas);
        } catch (Throwable t) {
            logger.error("single clear exception:", t);
        }
        return 0;
    }

    public static int readAlarm1(ModbusClient modbusClient, MeterModel meterModel) {
        try {
            int[] datas = modbusClient.ReadHoldingRegisters(meterModel.getAlarm1Pos(), 2);
            return mergeMeterDatas(meterModel, datas);

        } catch (Throwable t) {
            logger.error("single clear exception:", t);
        }
        return -1;
    }

    private static int mergeMeterDatas(MeterModel meterModel, int[] datas) {
        if (meterModel == MeterModel.ST76) {
            int mergeData = mergeBCDIntData(datas);
            return mergeData;
        } else {
            int mergeData = mergeIntData(datas);
            return mergeData;
        }
    }

    public static int writeAlarm1(ModbusClient modbusClient, MeterModel meterModel, String alarm1) {
        if (modbusClient == null) {
            return 0;
        }
        try {
            int[] datas = splitMeterData(meterModel, alarm1, false);
            modbusClient.WriteMultipleRegisters(meterModel.getAlarm1Pos(), datas);
            return 1;
        } catch (Throwable t) {
            logger.error("single clear exception:", t);
            return 0;
        }
    }

    public static int writeAlarm1(String comPort, MeterModel meterModel, String alarm1) {
        ModbusClient modbusClient = null;
        try {
            modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            if (modbusClient == null) {
                return 0;
            }
            int[] datas = splitMeterData(meterModel, alarm1, false);
            modbusClient.WriteMultipleRegisters(meterModel.getAlarm1Pos(), datas);
            return 1;
        } catch (Throwable t) {
            logger.error("write alarm1 exception:", t);
            return 0;
        } finally {
            ClientPool.getInstance().releaseModbusClient(comPort, modbusClient);
        }
    }

    public static int readAlarm2(ModbusClient modbusClient, MeterModel meterModel) {
        try {
            int[] datas = modbusClient.ReadHoldingRegisters(meterModel.getAlarm2Pos(), 2);
            return mergeMeterDatas(meterModel, datas);
        } catch (Throwable t) {
            logger.error("single clear exception:", t);
        }
        return -1;
    }

    public static int writeAlarm2(ModbusClient modbusClient, MeterModel meterModel, String alarm2) {
        if (modbusClient == null) {
            return 0;
        }
        try {
            int[] datas = splitMeterData(meterModel, alarm2, false);
            modbusClient.WriteMultipleRegisters(meterModel.getAlarm2Pos(), datas);
            return 1;
        } catch (Throwable t) {
            logger.error("write alarm2 exception:", t);
            return 0;
        }
    }

    public static int writeAlarm2(String comPort, MeterModel meterModel, String alarm2) {
        ModbusClient modbusClient = null;
        try {
            modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            if (modbusClient == null) {
                return 0;
            }
            int[] datas = splitMeterData(meterModel, alarm2, false);
            modbusClient.WriteMultipleRegisters(meterModel.getAlarm2Pos(), datas);
            return 1;
        } catch (Throwable t) {
            logger.error("write alarm1 exception:", t);
            return 0;
        } finally {
            ClientPool.getInstance().releaseModbusClient(comPort, modbusClient);
        }
    }

    public static int setPause(String comPort) {
        ModbusClient modbusClient = null;
        try {
            modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            if (modbusClient == null) {
                return 0;
            }
            modbusClient.WriteSingleRegister(OPERATE_POS, PAUSE);
            return 1;
        } catch (Throwable t) {
            logger.error("set pause exception:", t);
            return 0;
        } finally {
            ClientPool.getInstance().releaseModbusClient(comPort, modbusClient);
        }
    }

    public static int setPause(ModbusClient modbusClient){
        if (modbusClient == null) {
            return 0;
        }
        try {
            modbusClient.WriteSingleRegister(OPERATE_POS, PAUSE);
            return 1;
        } catch (Throwable t) {
            logger.error("set pause exception:", t);
            return 0;
        }
    }

    public static int setContinue(String comPort) {
        ModbusClient modbusClient = null;
        try {
            modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            if (modbusClient == null) {
                return 0;
            }
            modbusClient.WriteSingleRegister(OPERATE_POS, CONTINUE);
            return 1;
        } catch (Throwable t) {
            logger.error("set continue exception:", t);
            return 0;
        } finally {
            ClientPool.getInstance().releaseModbusClient(comPort, modbusClient);
        }
    }

    public static int setContinue(ModbusClient modbusClient){
        if (modbusClient == null) {
            return 0;
        }
        try {
            modbusClient.WriteSingleRegister(OPERATE_POS, CONTINUE);
            return 1;
        } catch (Throwable t) {
            logger.error("set continue exception:", t);
            return 0;
        }
    }

    public static int[] readData(ModbusClient modbusClient, int rowAddress, int bitNum) {
        try {
            int[] datas = modbusClient.ReadHoldingRegisters(rowAddress, bitNum);
//            int[] datas = modbusClient.ReadHoldingRegisters(0, 0x28);
            return datas;
        } catch (ModbusException e) {
            logger.error("ReadHoldingRegisters ModbusException:", e);
        } catch (IOException e) {
            logger.error("ReadHoldingRegisters IOException:", e);
        }
        return null;
    }

    public static int readDecimalDigitDatas(ModbusClient modbusClient, int decimalDigitPos) {
        int decimalDigit = 0;
        int[] decimalDigitDatas = ModbusHelper.readData(modbusClient, decimalDigitPos, 1);
        if (decimalDigitDatas.length > 0) {
            logger.info("deimalDigitDatas:{},{}", new Object[]{decimalDigitDatas[0], 1});
        }
        if (decimalDigitDatas != null && decimalDigitDatas.length > 0) {
            decimalDigit = decimalDigitDatas[0] & 0xf;
        }
        return decimalDigit;
    }

    public static int readDecimalDigitDatas(String comPort, int decimalDigitPos) throws ResultException {
        ModbusClient modbusClient = null;
        int decimalDigit = -1;
        try {
            modbusClient = ClientPool.getInstance().createModbusClient(comPort);
            if (modbusClient == null) {
                throw new ResultException("设备端口被占用，请检查后重试");
            }
            int[] decimalDigitDatas = ModbusHelper.readData(modbusClient, decimalDigitPos, 1);
            if (decimalDigitDatas.length > 0) {
                logger.info("deimalDigitDatas:{},{}", new Object[]{decimalDigitDatas[0], 1});
            }
            if (decimalDigitDatas != null && decimalDigitDatas.length > 0) {
                decimalDigit = decimalDigitDatas[0] & 0xf;
            }
            return decimalDigit;
        } catch (Throwable t) {
            logger.error("readDecimalDigitDatas exception:", t);
            throw new ResultException("连接设备异常，请检查后重试");
        } finally {
            ClientPool.getInstance().releaseModbusClient(comPort, modbusClient);
        }

    }

    public static int mergeIntData(int[] datas) {
        int data = (datas[0] & 0xffff) << 16;
        if (datas[0] < 0 ) {
            data = (0xffff + datas[0] + 1) << 16;
        }
        if (datas[1] < 0) {
            data = data + (0xffff + datas[1] + 1);
        } else {
            data = data + datas[1];
        }
        return data;
    }

    public static int mergeBCDIntData(int[] datas) {
        String hex1 = Integer.toHexString(datas[0]);
        int positiveNegativeFlag = 1;
        if (hex1.length() > 3) {
            String bit = hex1.substring(hex1.length() - 4, hex1.length() - 3);
            if (bit.equals("8") || bit.equals("9") || bit.equalsIgnoreCase("A") ) {
                positiveNegativeFlag = -1;
            }
            logger.info("bit :{}", new Object[]{bit});
        }
        if (hex1.length() > 2) {
            hex1 = hex1.substring(hex1.length() - 2, hex1.length());
        }

        String hex2 = Integer.toHexString(datas[1]);

        if (hex2.length() > 4) {
            hex2 = hex2.substring(hex2.length() - 4, hex2.length());
        }
        int data1 = Integer.parseInt(hex1) * 10000;
        int data2 = Integer.parseInt(hex2);
        int data = (data1 + data2) * positiveNegativeFlag;
        return data;
    }

    public static int[] splitMeterData(MeterModel meterModel, String value, boolean isScaleRate) {
        if (meterModel == MeterModel.ST76) {
            return splitBCDInt(value, meterModel, isScaleRate);
        } else {
            return splitInt(value, meterModel, isScaleRate);
        }
    }

    private static int[] splitInt(String szValue, MeterModel meterModel, boolean isScaleRate) {
        String szIntValue = szValue.replace(".", "");
        int value = Integer.parseInt(szIntValue);
        if (isScaleRate) {
            if (meterModel == MeterModel.ST76) { // * 100000
                value = value * 100000;
            } else {
                value = value * 10000;
            }
        }
        int high16Bit = ((value >> 16) & 0xffff);
        int low16Bit = (value & 0xffff);
        int[] datas = new int[]{high16Bit, low16Bit};
        return datas;
    }

    /**
     *
     * B
     * @param value
     * @param isScaleRate 是否是倍率, ST76下需要*100000后录入；CR76需要*10000后录入；
     * @return
     */
    private static int[] splitBCDInt(String value, MeterModel meterModel, boolean isScaleRate) {
        int decimalDigitIndex = value.indexOf(".");
        int symbolIndex = value.indexOf("-");
        int decimalDigitNum = 0;
        // 获取小数点位置
        if (symbolIndex > 0) {
            if (decimalDigitIndex >= 1) {
                decimalDigitNum = value.length() - decimalDigitIndex - 2;
            }
        } else {
            if (decimalDigitIndex >= 1) {
                decimalDigitNum = value.length() - decimalDigitIndex - 1;
            }
        }
        String headHex = generateBCDHeadHex(symbolIndex, decimalDigitNum);
        int iValue = 0;
        if (decimalDigitIndex >= 0) {
            float fValue = Float.parseFloat(value);
            if (isScaleRate) {
                if (meterModel == MeterModel.ST76) { // * 100000
                    iValue = (int) Math.floor(fValue * 100000);
                } else {
                    iValue = (int) Math.floor(fValue * 10000);
                }

            } else {
                iValue = (int) Math.floor(fValue);
            }
        } else {
            iValue= Integer.parseInt(value);
            if (isScaleRate) {
                if (meterModel == MeterModel.ST76) { // * 100000
                    iValue = iValue * 100000;
                } else {
                    iValue = iValue * 10000;
                }

            }
        }
        int positiveValue = Math.abs(iValue);
        int highBcd = ((positiveValue % 100000000) / 10000) % 100;
        int lowBcd = positiveValue % 10000;
        String hex1 = highBcd + "";
        if (hex1.length() > 3) {
            hex1 = headHex + hex1.substring(hex1.length() - 3, hex1.length());
        } else if (hex1.length() == 0) {
            hex1 = headHex + "00";
        } else if (hex1.length() == 1) {
            hex1 = headHex + "0" + hex1;
        } else if (hex1.length() == 2) {
            hex1 = headHex + hex1;
        }
        String hex2 = lowBcd + "";
        if (hex2.length() > 4) {
            hex2 = hex2.substring(hex2.length() - 4, hex2.length());
        }

        int high16Bit = Integer.parseInt(hex1, 16);
        int low16Bit = Integer.parseInt(hex2, 16);
        int[] datas = new int[]{high16Bit, low16Bit};
        return datas;
    }

    /**
     *
     * @param symbolIndex
     * @param decimalDigitNum 小数点后有几位数字
     * @return
     */
    private static String generateBCDHeadHex(int symbolIndex, int decimalDigitNum) {
        if (symbolIndex < 0) { // 正数
            if (decimalDigitNum  < 1) {
                return "00";
            } else if (decimalDigitNum == 1) {
                return "01";
            } else if (decimalDigitNum == 2) {
                return "02";
            } else if (decimalDigitNum == 3) {
                return "04";
            } else if (decimalDigitNum == 4) {
                return "08";
            } else if (decimalDigitNum == 5) {
                return "10";
            } else if (decimalDigitNum == 6) {
                return "20";
            }
        } else {
            if (decimalDigitNum  < 1) {
                return "80";
            } else if (decimalDigitNum == 1) {
                return "81";
            } else if (decimalDigitNum == 2) {
                return "82";
            } else if (decimalDigitNum == 3) {
                return "84";
            } else if (decimalDigitNum == 4) {
                return "88";
            } else if (decimalDigitNum == 5) {
                return "90";
            } else if (decimalDigitNum == 6) {
                return "A0";
            }
        }
        return "";
    }

//    private static int[] splitBCDInt(int value) {
//        int positiveValue = Math.abs(value);
//        int highBcd = ((value % 100000000) / 10000) % 100;
//        int lowBcd = value % 10000;
//        String hex1 = highBcd + "";
//        if (hex1.length() > 3) {
//            hex1 = hex1.substring(hex1.length() - 3, hex1.length());
//        }
//        String hex2 = lowBcd + "";
//        if (hex2.length() > 3) {
//            hex2 = hex2.substring(hex2.length() - 3, hex2.length());
//        }
//
//        int high16Bit = Integer.parseInt(hex1, 16);
//        int low16Bit = Integer.parseInt(hex2, 16);
//        int[] datas = new int[]{high16Bit, low16Bit};
//        return datas;
//    }

    public static void main(String[] args) {
        // 16544, //16816
//        ModbusHelper.bothClear("COM3");
//        int data = mergeIntData(new int[]{0, 64051});
//        System.out.println(data);
        System.out.println(Float.intBitsToFloat(1018444220));
    }
}
